using System;
using System.Collections.Concurrent;
using System.Threading;

namespace muduoDotNet
{
    /// <summary>
    /// 恢复管理器
    /// 负责处理连接的自动重连和错误恢复
    /// </summary>
    public class RecoveryManager : IDisposable
    {
        private readonly ConcurrentDictionary<string, RecoveryContext> _recoveryContexts;
        private readonly System.Threading.Timer _cleanupTimer;
        private readonly int _maxReconnectAttempts;
        private readonly int _reconnectIntervalMs;
        private readonly int _contextTimeoutMs;
        private bool _disposed = false;

        /// <summary>
        /// 连接恢复事件
        /// </summary>
        public event Action<TcpConnection, RecoveryResult> ConnectionRecovered;
        public event Action<TcpConnection, RecoveryResult> ConnectionRecoveryFailed;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="maxReconnectAttempts">最大重连尝试次数</param>
        /// <param name="reconnectIntervalMs">重连间隔（毫秒）</param>
        /// <param name="contextTimeoutMs">上下文超时时间（毫秒）</param>
        public RecoveryManager(int maxReconnectAttempts = 5, int reconnectIntervalMs = 5000, int contextTimeoutMs = 300000)
        {
            _maxReconnectAttempts = maxReconnectAttempts;
            _reconnectIntervalMs = reconnectIntervalMs;
            _contextTimeoutMs = contextTimeoutMs;
            
            _recoveryContexts = new ConcurrentDictionary<string, RecoveryContext>();
            _cleanupTimer = new System.Threading.Timer(CleanupExpiredContexts, null, _contextTimeoutMs, _contextTimeoutMs);
        }

        /// <summary>
        /// 注册连接以进行恢复管理
        /// </summary>
        /// <param name="connection">TCP连接</param>
        /// <param name="reconnectHandler">重连处理函数</param>
        public void RegisterConnection(TcpConnection connection, Func<TcpConnection, bool> reconnectHandler)
        {
            if (_disposed || connection == null || reconnectHandler == null)
                return;

            var context = new RecoveryContext
            {
                Connection = connection,
                ReconnectHandler = reconnectHandler,
                LastAttemptTime = DateTime.UtcNow,
                AttemptCount = 0,
                IsRecovering = false
            };

            _recoveryContexts.TryAdd(connection.Name, context);
            Logger.Debug($"Registered connection for recovery: {connection.Name}");
        }

        /// <summary>
        /// 启动连接恢复
        /// </summary>
        /// <param name="connection">TCP连接</param>
        public void StartRecovery(TcpConnection connection)
        {
            if (_disposed || connection == null)
                return;

            if (_recoveryContexts.TryGetValue(connection.Name, out var context))
            {
                if (!context.IsRecovering)
                {
                    context.IsRecovering = true;
                    context.AttemptCount = 0;
                    ScheduleReconnect(context);
                    Logger.Info($"Started recovery for connection: {connection.Name}");
                }
            }
            else
            {
                Logger.Warning($"No recovery context found for connection: {connection.Name}");
            }
        }

        /// <summary>
        /// 停止连接恢复
        /// </summary>
        /// <param name="connection">TCP连接</param>
        public void StopRecovery(TcpConnection connection)
        {
            if (_disposed || connection == null)
                return;

            if (_recoveryContexts.TryGetValue(connection.Name, out var context))
            {
                context.IsRecovering = false;
                Logger.Debug($"Stopped recovery for connection: {connection.Name}");
            }
        }

        /// <summary>
        /// 调度重连
        /// </summary>
        /// <param name="context">恢复上下文</param>
        private void ScheduleReconnect(RecoveryContext context)
        {
            if (_disposed || !context.IsRecovering)
                return;

            // 使用Timer进行延迟重连
            var timer = new System.Threading.Timer(ReconnectCallback, context, _reconnectIntervalMs, System.Threading.Timeout.Infinite);
            context.PendingTimer = timer;
        }

        /// <summary>
        /// 重连回调
        /// </summary>
        /// <param name="state">恢复上下文</param>
        private void ReconnectCallback(object state)
        {
            if (_disposed || !(state is RecoveryContext context) || !context.IsRecovering)
                return;

            try
            {
                context.PendingTimer?.Dispose();
                context.PendingTimer = null;

                context.AttemptCount++;
                context.LastAttemptTime = DateTime.UtcNow;

                Logger.Debug($"Attempting to reconnect... (attempt {context.AttemptCount}/{_maxReconnectAttempts}) for connection: {context.Connection?.Name}");

                // 尝试重连
                bool success = false;
                try
                {
                    success = context.ReconnectHandler(context.Connection);
                }
                catch (Exception ex)
                {
                    Logger.Error($"Reconnect handler threw exception: {ex.Message}", ex);
                }

                if (success)
                {
                    // 重连成功
                    context.IsRecovering = false;
                    context.AttemptCount = 0;
                    
                    var result = new RecoveryResult
                    {
                        Success = true,
                        AttemptCount = context.AttemptCount,
                        ElapsedTime = DateTime.UtcNow - context.LastAttemptTime,
                        ErrorMessage = null
                    };
                    
                    ConnectionRecovered?.Invoke(context.Connection, result);
                    Logger.Info($"Successfully reconnected connection: {context.Connection?.Name}");
                }
                else
                {
                    // 重连失败
                    if (context.AttemptCount < _maxReconnectAttempts)
                    {
                        // 继续尝试重连
                        ScheduleReconnect(context);
                    }
                    else
                        {
                        // 达到最大重试次数，放弃重连
                        context.IsRecovering = false;
                        
                        var result = new RecoveryResult
                        {
                            Success = false,
                            AttemptCount = context.AttemptCount,
                            ElapsedTime = DateTime.UtcNow - context.LastAttemptTime,
                            ErrorMessage = "Max reconnect attempts reached"
                        };
                        
                        ConnectionRecoveryFailed?.Invoke(context.Connection, result);
                        Logger.Error($"Failed to reconnect connection: {context.Connection?.Name} after {context.AttemptCount} attempts");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error($"Error in reconnect callback: {ex.Message}", ex);
                
                // 发生未处理的异常，继续尝试重连
                if (context.AttemptCount < _maxReconnectAttempts)
                {
                    ScheduleReconnect(context);
                }
                else
                {
                    context.IsRecovering = false;
                    
                    var result = new RecoveryResult
                    {
                        Success = false,
                        AttemptCount = context.AttemptCount,
                        ElapsedTime = DateTime.UtcNow - context.LastAttemptTime,
                        ErrorMessage = $"Unhandled exception: {ex.Message}"
                    };
                    
                    ConnectionRecoveryFailed?.Invoke(context.Connection, result);
                    Logger.Error($"Failed to reconnect connection: {context.Connection?.Name} due to unhandled exception");
                }
            }
        }

        /// <summary>
        /// 清理过期的上下文
        /// </summary>
        /// <param name="state">状态对象</param>
        private void CleanupExpiredContexts(object state)
        {
            if (_disposed)
                return;

            try
            {
                var now = DateTime.UtcNow;
                var expiredKeys = new ConcurrentBag<string>();

                foreach (var kvp in _recoveryContexts)
                {
                    var context = kvp.Value;
                    if (!context.IsRecovering && (now - context.LastAttemptTime).TotalMilliseconds > _contextTimeoutMs)
                    {
                        expiredKeys.Add(kvp.Key);
                    }
                }

                foreach (var key in expiredKeys)
                {
                    if (_recoveryContexts.TryRemove(key, out var context))
                    {
                        context.PendingTimer?.Dispose();
                        Logger.Debug($"Cleaned up expired recovery context: {key}");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error($"Error cleaning up expired contexts: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 获取连接的恢复状态
        /// </summary>
        /// <param name="connectionName">连接名称</param>
        /// <returns>恢复状态</returns>
        public RecoveryStatus GetRecoveryStatus(string connectionName)
        {
            if (_recoveryContexts.TryGetValue(connectionName, out var context))
            {
                return new RecoveryStatus
                {
                    IsRecovering = context.IsRecovering,
                    AttemptCount = context.AttemptCount,
                    LastAttemptTime = context.LastAttemptTime,
                    ConnectionState = context.Connection?.State ?? TcpConnectionState.Disconnected
                };
            }

            return new RecoveryStatus
            {
                IsRecovering = false,
                AttemptCount = 0,
                LastAttemptTime = DateTime.MinValue,
                ConnectionState = TcpConnectionState.Disconnected
            };
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _disposed = true;
                
                _cleanupTimer?.Dispose();
                
                foreach (var context in _recoveryContexts.Values)
                {
                    context.PendingTimer?.Dispose();
                }
                
                _recoveryContexts.Clear();
            }
        }
    }

    /// <summary>
    /// 恢复上下文
    /// </summary>
    internal class RecoveryContext
    {
        public TcpConnection Connection { get; set; }
        public Func<TcpConnection, bool> ReconnectHandler { get; set; }
        public DateTime LastAttemptTime { get; set; }
        public int AttemptCount { get; set; }
        public bool IsRecovering { get; set; }
        public System.Threading.Timer PendingTimer { get; set; }
    }

    /// <summary>
    /// 恢复结果
    /// </summary>
    public class RecoveryResult
    {
        public bool Success { get; set; }
        public int AttemptCount { get; set; }
        public TimeSpan ElapsedTime { get; set; }
        public string ErrorMessage { get; set; }
    }

    /// <summary>
    /// 恢复状态
    /// </summary>
    public class RecoveryStatus
    {
        public bool IsRecovering { get; set; }
        public int AttemptCount { get; set; }
        public DateTime LastAttemptTime { get; set; }
        public TcpConnectionState ConnectionState { get; set; }
    }
}